#include "global_rainbow_table.h"
#include <time.h>
#include "../global/global_declarations.h"
#include "stdlib.h"
#include "string.h"
#include "ctype.h"
#include "stdio.h"
#include "../global/misc.h"
#include "../DEHT/deht.h"
#include "../global/global_auth.h"
#include "../exhaustive_search/rule.h"

/**
 * does the hash chaining.
 * do_first_hashing
 */
void do_chain_hashing(int chain_length, String* first_pass, int64* seed_arr,
		BasicHashFunctionPtr hash_func, int hash_length, unsigned char* dest,
		bool do_first_hashing, int starting_index) {
	int i;
	int64 num_pass;
	String* str_pass = make_empty_string();
	unsigned char cur_hash [MAX_HASH_LENGTH] = {0};
	unsigned char cur_hash_hex [MAX_HASH_LENGTH*2] = {0};
	if (do_first_hashing) {
		hash_func((unsigned char*) first_pass->data, first_pass->size, cur_hash);
		binary2hexa(cur_hash, hash_length, (char*)cur_hash_hex, hash_length*2);
	}
	else {
		memcpy((char*) cur_hash, (char*) first_pass->data, hash_length);
	}
	for (i = starting_index; i < chain_length; ++i) {
		/* reduction */
		num_pass = pseudo_random_function(cur_hash, hash_length, seed_arr[i]);
		int64_to_word(num_pass, str_pass);
		/* hash function */
		hash_func((unsigned char*) str_pass->data, str_pass->size,
				(unsigned char*) cur_hash);
		String_reset(str_pass);
	}
	memcpy(dest, cur_hash, hash_length);
	String_free(str_pass);
}

/**
 * returns the equivalent word from the rule.
 */
void int64_to_word(int64 src, String* dest) {
	int64 rule_size;
	rule_size = count_words_in_whole_rule(the_rule, the_dictionary);
	src = src % rule_size;
	String_reset(dest);
	word_k_for_rule(src, dest);
}

/**
 * reads the ini file and gets it's parameter.
 * return true if succeeded and false (+throws error) if not.
 */
bool read_ini_file(IniFileContent* /* out */content, char* ini_file_name) {
	char line[120];
	char* p_equals_sign;
	char* word;
	bool has_filename = 0, has_chain_length = 0, has_entries = 0,
			has_bucket_length = 0, has_rule = 0, has_seed = 0;

	FILE* ini_file = fopen(ini_file_name, "r");
	if (ini_file == 0) {
		perror(ini_file_name);
	}

	while (fgets(line, sizeof(line), ini_file) != 0) {
		p_equals_sign = strchr(line, '=');
		if (p_equals_sign == 0) {
			if (strlen(line) == 0) {
				continue;
			} else {
				fprintf(stderr, "bad line format - no \" sign in line: \'%s\'",
					line);
			}
		}
		word = strtok(line, "= \t\n");
		if (strcmp(word, "dictionary_name") == 0) {
			word = strtok(0, "= \t\n");
			strcpy(&content->dictionary_filename[0], word);
			has_filename = 1;
		} else if (strcmp(word, "rainbow_chain_length") == 0) {
			word = strtok(0, "= \t\n");
			if (!is_number(word)) {
				fprintf(
						stderr,
						"error in read_ini_file: bad ini file format in line \'%s\': rainbow_chain_length should be a number",
						line);
				exit(0);
			}
			content->rainbow_chain_length = atoi(word);
			has_chain_length = 1;
		} else if (strcmp(word, "entries_in_hash_table") == 0) {
			word = strtok(0, "= \t\n");
			if (!is_number(word)) {
				fprintf(
						stderr,
						"error in read_ini_file: bad ini file format in line \'%s\': entries_in_hash_table should be a number",
						line);
				exit(0);
			}
			content->entries_in_hash_table = atoi(word);
			has_entries = 1;
		} else if (strcmp(word, "bucket_block_length") == 0) {
			word = strtok(0, "= \t\n");
			if (!is_number(word)) {
				fprintf(
						stderr,
						"error in read_ini_file: bad ini file format in line \'%s\': bucket_block_length should be a number",
						line);
				exit(0);
			}
			content->bucket_block_length = atoi(word);
			has_bucket_length = 1;
		} else if (strcmp(word, "rule") == 0) {
			word = strtok(0, "= \t\n");
			if (!validate_rule(word)) {
				fprintf(
						stderr,
						"error in read_ini_file: bad ini file format in line \'%s\': invalid rule",
						line);
				exit(0);
			}
			strcpy(&content->rule[0], word);
			has_rule = 1;
		} else if (strcmp(word, "main_random_seed") == 0) {
			word = strtok(0, "= \t\n");
			strcpy(&content->main_random_seed[0], word);
			has_seed = 1;
		}
	} /* end while */
	if (!(has_bucket_length && has_chain_length && has_entries && has_filename
			&& has_rule && has_seed)) {
		fprintf(stderr,
				"error in read_ini_file: missing information in ini file");
		exit(0);
	}
	return true;
}
